#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>

#include <ijpegd_common.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
static int ijpegd_validate_parameters(ijpegd_t *h)
{
	/* soc, buf, device manage */
	if (h->param.soc < C_M200 || h->param.soc >= C_SOC_MAX) {
		ijpegd_log(h, C_LOG_ERROR, "unsupported soc:%d\n", h->param.soc);
		goto err_soc;
	}

	if ((h->param.i_csp != C_CSP_NV12) && (h->param.i_csp != C_CSP_T420)) {
		ijpegd_log(h, C_LOG_ERROR, "invalid final decode color space:%d (only support NV12"
			" or T420)\n", h->param.i_csp);
		goto err_csp;
	}

	if (h->param.i_max_width < 256 || h->param.i_max_height < 16) {
		ijpegd_log(h, C_LOG_ERROR, "too small width=%d,height=%d\n", h->param.i_max_width, h->param.i_max_height);
		goto err_max_resolution;
	}

	if (h->param.b_tlb && ((h->param.ckmalloc == NULL) || (h->param.ckfree == NULL) || (h->param.ckvirt_to_phys == NULL)
			|| (h->param.ckphys_to_virt == NULL))) {
		h->param.ckmalloc = c_malloc;
		h->param.ckfree = c_free;
		h->param.ckvirt_to_phys = c_virt_to_phys;
		h->param.ckphys_to_virt = c_phys_to_virt;
	}  else if ((h->param.ckmalloc == NULL) || (h->param.ckfree == NULL)
		 || (h->param.ckvirt_to_phys == NULL) || (h->param.ckphys_to_virt == NULL)) {
		h->param.ckmalloc = c_kalloc;
		h->param.ckfree = c_kfree;
		h->param.ckvirt_to_phys = c_kvirt_to_kphys;
		h->param.ckphys_to_virt = c_kphys_to_kvirt;
	}

	return 0;
#define BOOLIFY(x) h->param.x = !!h->param.x
	BOOLIFY( b_tlb );
	BOOLIFY( b_use_phy );
#undef BOOLIFY

err_max_resolution:
err_csp:
err_soc:
	return -1;
}

ijpegd_t *ijpegd_init(ijpegd_param_t *param)
{
	ijpegd_t *h = NULL;

	if ((h = malloc(sizeof(*h))) == NULL) {
		ijpegd_log(NULL, C_LOG_ERROR, "malloc ijpegd_t var h failed:%s\n", strerror(errno));
		goto err_malloc_h;
	}
	memset(h, 0, sizeof(*h));
	memcpy(&h->param, param, sizeof(ijpegd_param_t));

	if (ijpegd_validate_parameters(h) < 0) {
		ijpegd_log(NULL, C_LOG_ERROR, "ijpegd_validate_parameters failed\n");
		goto err_validate_parameters;
	}
	c_mc_init(&h->mc);

	if (ijpegd_icodec_init(h) < 0) {
		ijpegd_log(NULL, C_LOG_ERROR, "icodec_init failed\n");
		goto err_icodec_init;
	}

	if (ijpegd_icodec_request_vbinfo(h)) {
		ijpegd_log(NULL, C_LOG_ERROR, "icodec_get_vbinfo failed\n");
		goto err_icodec_request_vbinfo;
	}

	return h;

err_icodec_request_vbinfo:
	ijpegd_icodec_deinit(h);
err_icodec_init:
err_validate_parameters:
	free(h);
err_malloc_h:
	return NULL;
}

void ijpegd_deinit(ijpegd_t *h)
{
	ijpegd_icodec_release_vbinfo(h);
	ijpegd_icodec_deinit(h);
	free(h);
}

int ijpegd_decode(ijpegd_t *h, ijpegd_pic_t *pic_out, ijpegd_nal_t *pnal_in)
{
	if (!h || !pic_out || !pic_out->virAddr || !pnal_in || !pnal_in->p_payload || !pnal_in->i_payload) {
		ijpegd_log(h, C_LOG_ERROR, "%s is NULL\n",
				!h ? "h" : (!pic_out ? "pic_out" : (!pic_out->virAddr ? "pic_out->virAddr" :
						(!pnal_in ? "pnal_in" : (!pnal_in->p_payload ? "pnal_in->p_payload" : "pnal_in->i_payload")))));
		goto err_invalide_param;
	}
	ijpegd_decode_header(h, pnal_in->p_payload, pnal_in->i_payload);

	if (pic_out->size < (h->dst_w * h->dst_h * 3 / 2)) {
		ijpegd_log(h, C_LOG_ERROR, "pic_out->size=%d, h->dst_w*h->dst_h*3/2=%d failed\n", pic_out->size, h->dst_w*h->dst_h*3/2);
		goto err_pic_out_size;
	}

	h->dst_y = (uint8_t *)pic_out->virAddr;
	h->dst_c = h->dst_y + h->dst_w * h->dst_h;
	pic_out->img.i_csp = h->param.i_csp;
	pic_out->img.i_plane = 2;
	pic_out->img.i_stride[0] = h->dst_w;
	pic_out->img.i_stride[1] = h->dst_w;
	pic_out->img.i_lines[0] = h->dst_h;
	pic_out->img.i_lines[1] = h->dst_h / 2;
	pic_out->img.alloc_plane[0] = h->dst_y;
	pic_out->img.alloc_plane[1] = h->dst_c;
	pic_out->img.plane[0] = h->dst_y;
	pic_out->img.plane[1] = h->dst_c;
	pic_out->img.is_vir = 1;

	if (ijpegd_icodec_dec(h) < 0) {
		ijpegd_log(h, C_LOG_ERROR, "ijpegd_icodec_dec failed\n");
		goto err_icodec_enc;
	}
	pixel *src[4] = {h->fenc.plane[0], h->fenc.plane[1], 0, 0};
	int i_src_stride[4] = {h->dst_w, h->dst_w, 0, 0};
	h->mc.copy_frame[c_mc_type(h->param.i_csp, C_CSP_T420)](pic_out->img.plane, (int *)pic_out->img.i_stride,
			src, i_src_stride, h->dst_w, h->dst_h);
	pic_out->size = h->icodec_io.bslen * 256 * 3 / 2;

	return 0;

err_icodec_enc:
err_pic_out_size:
err_invalide_param:
	return -1;
}
